package fr.ece.ing4.si.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Event;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

import fr.ece.ing4.si.multithread.ConcurerentMonteCarlo;
import fr.ece.ing4.si.singlethread.MonteCarlo;
import fr.ece.ing4.si.singlethread.SequentialMonteCarlo;

public class MainView extends JFrame implements ActionListener {

	
	private static final long serialVersionUID = 1L;
	private JPanel myContainer;
	private JPanel myGridOption;
	private JPanel mySimulDisplay;
	private JPanel myGridSimu;
	private ArrayList<JLabel> myLabels;
	private ArrayList<JComboBox> myComboBox;
	private JLabel execTime, output;
	private JButton launch, reset;
	private GridBagConstraints c;
	private JProgressBar myProgressBar;
	private long start, end;
	private double res, resSim;
	private String choice;
	private ArrayList<MonteCarlo> data;
	private Thread barThread=null, calc;
/**
 * Set the Main view 
 * @param data The value of the parameters retrieved from the file
 * @param choice Specifies whether it is The multiThreaded or SingleThreaded version
 * @throws IOException
 */
	public MainView(ArrayList<MonteCarlo> data, String choice) throws IOException {
		
		super("Monte Carlo " + choice);
		this.data = data;
		this.choice=choice;
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		
		c = new GridBagConstraints();
		myContainer = new JPanel(new BorderLayout(5,5));
		myGridOption = new JPanel(new GridBagLayout());
		mySimulDisplay = new JPanel(new BorderLayout(50, 50));
		myContainer.add(myGridOption, BorderLayout.CENTER);
		myContainer.add(mySimulDisplay, BorderLayout.EAST);
		
		myComboBox = new ArrayList<JComboBox>();
		InitComboBox(data);
		myLabels = new ArrayList<JLabel>();
		InitLabel();
		InitMySimuDisplay();
		InitMyButtonsDisplay(choice);	
		
		this.setSize(800, 450);
		this.setContentPane(myContainer);
		this.setVisible(true);
		this.setResizable(false);
	    this.setLocationRelativeTo(null);
	}
	
/**
 * Initialize the value in each Combobox with the paramters retrieve from the file
 * @param data The value of the parameters retrieved from the file
 * @throws IOException
 */
	public void InitComboBox(ArrayList<MonteCarlo> data) throws IOException {
		
		int k=0;
		
		for (int i = 0; i < 9; i++) {
			myComboBox.add(new JComboBox());
		}
				//Only two available options.. c or p
				myComboBox.get(0).addItem("c");
				myComboBox.get(0).addItem("p");
		for (MonteCarlo m : data) {
			int i=0;
				myComboBox.get(++i).addItem(m.getS());
				myComboBox.get(++i).addItem(m.getX());
				myComboBox.get(++i).addItem(m.getT());
				myComboBox.get(++i).addItem(m.getR());
				myComboBox.get(++i).addItem(m.getB());
				myComboBox.get(++i).addItem(m.getV());
				myComboBox.get(++i).addItem(m.getnSteps());
				myComboBox.get(++i).addItem(m.getnSimulations());
			}
		for(JComboBox comb : myComboBox){
			c.gridx = 1;
			c.gridy = k;
			c.fill = GridBagConstraints.HORIZONTAL;
			c.insets = new Insets(10, 10, 0, 0);
			myGridOption.add(comb, c);
			k++;
		}
		
	}
	
/**
 * Initialize the labels
 */
	void InitLabel(){
		int k=0;
		myLabels.add(new JLabel("Specifies wether the option is a call or put option"));
		myLabels.add(new JLabel("The current Price of the option underlying asset"));
		myLabels.add(new JLabel("The Strike Price of the option"));
		myLabels.add(new JLabel("Time of maturity of the option (1= 1year)"));
		myLabels.add(new JLabel("The discount rate (risk-free interest rate"));
		myLabels.add(new JLabel("The cost of carry rate (cost of interest, dividends and any other additional costs)"));
		myLabels.add(new JLabel("The volatility of the underlying asset (the standard deviation of its market price)"));
		myLabels.add(new JLabel("Number of steps to execute for each option price path"));
		myLabels.add(new JLabel("Number of simulations to run"));
		
		for(JLabel lab : myLabels){
			c.gridx = 0;
			c.gridy = k;
			c.fill = GridBagConstraints.HORIZONTAL;
			c.insets = new Insets(10, 10, 0, 0);
			myGridOption.add(lab, c);
			k++;
		}
	}
	/**
	 * Initialize the ProgressBar and the Label for the Execution time and the output
	 */
	public void InitMySimuDisplay(){
		execTime = new JLabel();
		output = new JLabel();
		myGridSimu = new JPanel(new GridLayout(0,1));
		myGridSimu.add(new JLabel(""));
		myGridSimu.add(new JLabel(""));
		myGridSimu.add(new JLabel(""));
		myGridSimu.add(new JLabel("Output of MonteCarlo:"));
		myGridSimu.add(output);
		myGridSimu.add(new JLabel(""));
		myGridSimu.add(new JLabel(""));
		myGridSimu.add(new JLabel("Time of Execution: "));
		myGridSimu.add(execTime);
		myGridSimu.add(new JLabel(""));
		myGridSimu.add(new JLabel(""));
		myGridSimu.add(new JLabel("Time remaining:"));
		myProgressBar = new JProgressBar(1, 100);
		myProgressBar.setName("Time remaining");
		myProgressBar.setForeground(Color.BLUE);
		myProgressBar.setBackground(new Color(160, 255, 160, 255));
		myProgressBar.setStringPainted(true);
		myGridSimu.add(myProgressBar);
		
		mySimulDisplay.add(myGridSimu, BorderLayout.WEST);
		
	}
	
	/**
	 * Initialize the main button of the View
	 * @param choice Specifies whether it is The MultiThreaded or SingleThreaded
	 */
	public void InitMyButtonsDisplay(String choice){
		JPanel flow = new JPanel(new FlowLayout());
		JPanel flow2 = new JPanel(new FlowLayout());
		
		launch = new JButton("Launch Simulation");
		reset = new JButton("Reset Initial values");
		launch.addActionListener(this);
		reset.addActionListener(this);
		flow.add(launch);
		flow.add(reset);
		flow2.add(new JLabel("Welcome to the " + choice + " MonteCarlo Simulation!"));
		myContainer.add(flow2, BorderLayout.NORTH);
		myContainer.add(flow, BorderLayout.SOUTH);
	}
/**
 * Action performed by the user, retrieve the {@link Event} and do the corresponding action
 */
	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource().equals(launch)){
			
			MonteCarlo.progression=0;
			calc = new Thread(new Calculate());
			calc.start();
			
			myProgressBar.setMaximum(Integer.parseInt(myComboBox.get(8).getSelectedItem().toString()));
			if(barThread==null){
				barThread = new Thread(new PJbarThread());
				barThread.start();
			}
		}
		if(e.getSource().equals(reset)){
			int i=0;
			output.setText("");
			execTime.setText("");
			MonteCarlo.progression=0;
			myComboBox.get(i).setSelectedItem(data.get(0).getCallPutFlag());
			myComboBox.get(++i).setSelectedItem(data.get(0).getS());
			myComboBox.get(++i).setSelectedItem(data.get(0).getX());
			myComboBox.get(++i).setSelectedItem(data.get(0).getT());
			myComboBox.get(++i).setSelectedItem(data.get(0).getR());
			myComboBox.get(++i).setSelectedItem(data.get(0).getB());
			myComboBox.get(++i).setSelectedItem(data.get(0).getV());
			myComboBox.get(++i).setSelectedItem(data.get(0).getnSteps());
			myComboBox.get(++i).setSelectedItem(data.get(0).getnSimulations());
		}
	}
	
	
	private class PJbarThread implements Runnable{
/**
 * This inner class is here to refresh the {@link JProgressBar} while the computation is still running. The inner class allows the Thread to still access
 * to the {@link JProgressBar} and updates it
 */
		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(true){
				myProgressBar.setValue(MonteCarlo.progression);
			}
		}
	}
	
	private class Calculate implements Runnable{
		/**
		 * This inner class is the one which allows to compute the simulation without freezing the View while computing. We do the computation in this 
		 * main thread to be able to avoid that.
		 */
		@Override
		public void run() {
			// TODO Auto-generated method stub
			
			MonteCarlo m = new MonteCarlo();
			m.setCallPutFlag(myComboBox.get(0).getSelectedItem().toString());
			m.setS(Double.parseDouble(myComboBox.get(1).getSelectedItem().toString()));
			m.setX(Double.parseDouble(myComboBox.get(2).getSelectedItem().toString()));
			m.setT(Double.parseDouble(myComboBox.get(3).getSelectedItem().toString()));
			m.setR(Double.parseDouble(myComboBox.get(4).getSelectedItem().toString()));
			m.setB(Double.parseDouble(myComboBox.get(5).getSelectedItem().toString()));
			m.setV(Double.parseDouble(myComboBox.get(6).getSelectedItem().toString()));
			m.setnSteps(Integer.parseInt(myComboBox.get(7).getSelectedItem().toString()));
			m.setnSimulations(Integer.parseInt(myComboBox.get(8).getSelectedItem().toString()));
			System.out.println(m);
			
			if(choice.equals("SingleThread")){
				start = System.nanoTime();
				resSim = SequentialMonteCarlo.compute(m);
			}
				
			else if(choice.equals("MultiThread")){
				try {
					start = System.nanoTime();
					resSim = ConcurerentMonteCarlo.compute(m);
				} catch (InterruptedException | ExecutionException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
				
			end = System.nanoTime();
			res = (end - start)/1.0e9;
			
			output.setText(Double.toString(resSim));
			execTime.setText(Double.toString(res)+" seconds");
			}
		}
}
